#include "CppUTest/TestHarness.h"

extern "C"
{
#include "can_if.h"
#include "can_virtual.h"
#include "hdf_log.h"
#include "osal_mem.h"
#include "osal_thread.h"
#include "osal_time.h"
}

#define TEST_CAN_BUS_NUM 31

#define CAN_TEST_ID_A 0x15A
#define CAN_TEST_ID_B 0x2A5
#define CAN_TEST_ID_C 0x555
#define CAN_MASK_FULL 0x1FFFFFFF
#define CAN_TEST_DATA 0xAB

static struct HdfDeviceObject hdfDev = {
    .service = NULL,
    .property = NULL,
    .priv = NULL,
};

static DevHandle g_handle;
static struct CanMsg g_msgA;
static struct CanMsg g_msgB;
static struct CanMsg g_msgC;

static struct CanFilter g_filterA = {
    .rtr = 0,
    .ide = 0,
    .id = CAN_TEST_ID_A,
    .rtrMask = 1,
    .ideMask = 1,
    .idMask = CAN_MASK_FULL,
};

static struct CanFilter g_filterB = {
    .rtr = 0,
    .ide = 0,
    .id = CAN_TEST_ID_B,
    .rtrMask = 1,
    .ideMask = 1,
    .idMask = CAN_MASK_FULL,
};

static void CanMsgInitByParms(struct CanMsg *msg, uint32_t id, uint32_t ide, uint32_t rtr, uint8_t data)
{
    int i;
    msg->ide = ide;
    msg->id = id;
    msg->rtr = rtr;
    for (i = 0; i < (uint8_t)(sizeof(msg->data)); i++) {
        msg->data[i] = data;
    }
    msg->dlc = i;
    msg->error = 0;
}

TEST_GROUP(can_if_test)
{
    void setup()
    {
        struct HdfDriverEntry *entry = CanVirtualGetEntry();
        LONGS_EQUAL(HDF_SUCCESS, entry->Init(&hdfDev));
        LONGS_EQUAL(HDF_SUCCESS, CanBusOpen(TEST_CAN_BUS_NUM, &g_handle));
        CHECK_FALSE(g_handle == NULL);
        CanMsgInitByParms(&g_msgA, CAN_TEST_ID_A, 0, 0, CAN_TEST_DATA);
        CanMsgInitByParms(&g_msgB, CAN_TEST_ID_B, 0, 0, CAN_TEST_DATA);
        CanMsgInitByParms(&g_msgC, CAN_TEST_ID_C, 0, 0, CAN_TEST_DATA);
    }

    void teardown()
    {
        CanBusClose(g_handle);
        struct HdfDriverEntry *entry = CanVirtualGetEntry();
        entry->Release(&hdfDev);
    }
};

TEST(can_if_test, should_multi_open_can_bus_by_number)
{
    DevHandle handle = NULL;
    LONGS_EQUAL(HDF_SUCCESS, CanBusOpen(TEST_CAN_BUS_NUM, &handle));
    CHECK_FALSE(handle == NULL);
    CanBusClose(handle);
}

static bool CanMsgEqual(const struct CanMsg *msgA, const struct CanMsg *msgB)
{
    int i;
    if (msgA->ide != msgB->ide) {
        return false;
    }
    if (msgA->id != msgB->id) {
        return false;
    }
    if (msgA->rtr != msgB->rtr) {
        return false;
    }
    if (msgA->dlc != msgB->dlc) {
        return false;
    }
    for (i = 0; i < msgA->dlc; i++) {
        if (msgA->data[i] != msgB->data[i]) {
            return false;
        }
    }
    return true;
}

static bool CanBusCanNotReadMsg(DevHandle handle, struct CanMsg *msg)
{
    struct CanMsg msgGot;
    LONGS_EQUAL(HDF_ERR_TIMEOUT, CanBusReadMsg(handle, &msgGot, 10));
    return true;
}

static bool CanBusCanReadMsg(DevHandle handle, struct CanMsg *msg)
{
    struct CanMsg msgGot;
    LONGS_EQUAL(HDF_SUCCESS, CanBusReadMsg(handle, &msgGot, 10));
    CHECK_TRUE(CanMsgEqual(msg, &msgGot));
    return true;
}

static bool CanBusCanSendAndReadMsg(DevHandle handle, struct CanMsg *msg)
{
    LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(handle, msg));
    return CanBusCanReadMsg(handle, msg);
}

TEST(can_if_test, should_send_and_read_msg_success)
{
    CHECK_TRUE(CanBusCanSendAndReadMsg(g_handle, &g_msgA));
}

TEST(can_if_test, should_return_immediately_when_read_none_block)
{
    struct CanMsg msg;
    CHECK_FALSE(HDF_ERR_TIMEOUT == CanBusReadMsg(g_handle, &msg, 0));
}

TEST(can_if_test, should_return_timeout_when_read_block)
{
    struct CanMsg msg;
    LONGS_EQUAL(HDF_ERR_TIMEOUT, CanBusReadMsg(g_handle, &msg, 10));
}

TEST(can_if_test, should_add_and_del_filter)
{
    LONGS_EQUAL(HDF_SUCCESS, CanBusAddFilter(g_handle, &g_filterA));
    LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(g_handle, &g_msgA));
    LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(g_handle, &g_msgB));
    CHECK_TRUE(CanBusCanReadMsg(g_handle, &g_msgA));
    CHECK_TRUE(CanBusCanNotReadMsg(g_handle, &g_msgB)); // filter out ...

    LONGS_EQUAL(HDF_SUCCESS, CanBusDelFilter(g_handle, &g_filterA));
    LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(g_handle, &g_msgB));
    CHECK_TRUE(CanBusCanReadMsg(g_handle, &g_msgB)); // filter out ...
}

TEST(can_if_test, should_add_multi_filters)
{
    LONGS_EQUAL(HDF_SUCCESS, CanBusAddFilter(g_handle, &g_filterA));
    LONGS_EQUAL(HDF_SUCCESS, CanBusAddFilter(g_handle, &g_filterB));

    LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(g_handle, &g_msgA));
    LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(g_handle, &g_msgB));
    LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(g_handle, &g_msgC));

    CHECK_TRUE(CanBusCanReadMsg(g_handle, &g_msgA));
    CHECK_TRUE(CanBusCanReadMsg(g_handle, &g_msgB));
    CHECK_TRUE(CanBusCanNotReadMsg(g_handle, &g_msgC));

    LONGS_EQUAL(HDF_SUCCESS, CanBusDelFilter(g_handle, &g_filterA));
    LONGS_EQUAL(HDF_SUCCESS, CanBusDelFilter(g_handle, &g_filterB));

    CHECK_TRUE(CanBusCanSendAndReadMsg(g_handle, &g_msgC));
}

TEST(can_if_test, should_set_and_get_config)
{
    int32_t ret;
    struct CanConfig cfgOld, cfgNew, cfgGot;
    LONGS_EQUAL(HDF_SUCCESS, CanBusGetCfg(g_handle, &cfgOld));
    cfgNew = cfgOld;
    cfgNew.speed = 2000 * 10;
    cfgNew.mode = CAN_BUS_LOOPBACK;
    ret = CanBusSetCfg(g_handle, &cfgNew);
    CHECK_TRUE(ret == HDF_SUCCESS || ret == HDF_ERR_NOT_SUPPORT);
    LONGS_EQUAL(HDF_SUCCESS, CanBusGetCfg(g_handle, &cfgGot));
    if (ret == HDF_SUCCESS) {
        CHECK_TRUE(cfgGot.speed == cfgNew.speed && cfgGot.mode == cfgNew.mode);
    } else {
        CHECK_TRUE(cfgGot.speed == cfgOld.speed && cfgGot.mode == cfgOld.mode);
    }
    LONGS_EQUAL(HDF_SUCCESS, CanBusSetCfg(g_handle, &cfgOld));
}

TEST(can_if_test, should_get_state_unless_not_supported)
{
    int32_t ret;
    ret = CanBusGetState(g_handle);
    if (ret == HDF_ERR_NOT_SUPPORT) {
        return;
    }
    CHECK_TRUE(ret >= CAN_BUS_RESET && ret < CAN_BUS_INVALID);
}

#define CAN_TEST_STACK_SIZE    (1024 * 10)
static struct OsalThread *CanTestStartTestThread(OsalThreadEntry entry, DevHandle handle)
{
    int32_t ret;
    struct OsalThreadParam threadCfg;
    struct OsalThread *thread = (struct OsalThread *)OsalMemCalloc(sizeof(*thread));;

    ret = OsalThreadCreate(thread, (OsalThreadEntry)entry, (void *)handle);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("create test thread fail:%d", ret);
        return NULL;
    }

    threadCfg.name = (char *)"CanTestPoller";
    threadCfg.priority = OSAL_THREAD_PRI_DEFAULT;
    threadCfg.stackSize = CAN_TEST_STACK_SIZE;

    ret = OsalThreadStart(thread, &threadCfg);
    if (ret != HDF_SUCCESS) {
        (void)OsalThreadDestroy(thread);
        HDF_LOGE("start test thread2 fail:%d", ret);
        return NULL;
    }

    return thread;
}

static void CanTestStopTestThread(struct OsalThread *thread)
{
    if (thread == NULL) {
        return;
    }
    LONGS_EQUAL(HDF_SUCCESS, OsalThreadDestroy(thread));
    OsalMemFree(thread);
}

static int CanTestReaderFunc(void *param)
{
    struct CanMsg msg;
    DevHandle handle = (DevHandle)param;

    LONGS_EQUAL(HDF_SUCCESS, CanBusReadMsg(handle, &msg, 10));
    return HDF_SUCCESS;
}

TEST(can_if_test, should_read_msg_success_in_another_thread_by_the_same_handle)
{
    struct CanMsg msgGot;
    struct OsalThread *thread = NULL;
    thread = CanTestStartTestThread(CanTestReaderFunc, g_handle);
    CHECK_FALSE(thread == NULL);
    LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(g_handle, &g_msgA));
    OsalMSleep(20);
    CHECK_FALSE(CanBusReadMsg(g_handle, &msgGot, 0) == HDF_SUCCESS);
    CanTestStopTestThread(thread);
}

static int CanTestSenderFunc(void *param)
{
    DevHandle handle = (DevHandle)param;

    HDF_LOGE("can bus send msg begin:%p", &g_msgA);
    int32_t ret = CanBusSendMsg(handle, &g_msgA);
    //LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(handle, &g_msgA));
    LONGS_EQUAL(HDF_SUCCESS, ret);
    HDF_LOGE("can bus send msg end:%p", &g_msgA);
    return HDF_SUCCESS;
}

TEST(can_if_test, should_send_msg_success_in_another_thread_by_the_same_handle)
{
    struct CanMsg msgGot;
    struct OsalThread *thread = NULL;

    thread = CanTestStartTestThread(CanTestSenderFunc, g_handle);
    CHECK_FALSE(thread == NULL);
    LONGS_EQUAL(HDF_SUCCESS, CanBusReadMsg(g_handle, &msgGot, 10));
    CanTestStopTestThread(thread);
}

TEST(can_if_test, should_read_msg_success_in_another_thread_by_another_handle)
{
    struct CanMsg msgGot;
    struct OsalThread *thread = NULL;
    DevHandle handle = NULL;

    LONGS_EQUAL(HDF_SUCCESS, CanBusOpen(TEST_CAN_BUS_NUM, &handle));
    thread = CanTestStartTestThread(CanTestReaderFunc, handle);
    CHECK_FALSE(thread == NULL);
    LONGS_EQUAL(HDF_SUCCESS, CanBusSendMsg(g_handle, &g_msgA));
    OsalMSleep(20);
    LONGS_EQUAL(HDF_SUCCESS, CanBusReadMsg(g_handle, &msgGot, 0));
    CanTestStopTestThread(thread);
    CanBusClose(handle);
}

TEST(can_if_test, should_send_msg_success_in_another_thread_by_another_handle)
{
    struct CanMsg msgGot;
    struct OsalThread *thread = NULL;
    DevHandle handle = NULL;

    LONGS_EQUAL(HDF_SUCCESS, CanBusOpen(TEST_CAN_BUS_NUM, &handle));
    thread = CanTestStartTestThread(CanTestSenderFunc, g_handle);
    CHECK_FALSE(thread == NULL);
    LONGS_EQUAL(HDF_SUCCESS, CanBusReadMsg(handle, &msgGot, 10));
    LONGS_EQUAL(HDF_SUCCESS, CanBusReadMsg(g_handle, &msgGot, 10));
    CanTestStopTestThread(thread);
    CanBusClose(handle);
}
